from build_task import BuildTask

import hashlib
import os
import os.path

class Builder(object):
  """Performs the actual build of tasks."""
  
  def __init__(self, bld):
    """Initializes the builder."""
    self.tasks = []
    self.bld = bld
    
  def build(self, build_task):
    """Builds the given task and all its recursive dependencies."""
    self._find_dependencies(build_task)
    for task in self.tasks:
      self._expand_dependencies(task)
      self._build_task(task)

  def _find_dependencies(self, task):
    if task._state == 'scheduled' or task._state == 'built':
      return
    if task._state == 'expanding':
      raise RuntimeException('Detected cycles')
    task._state = 'expanding'
    for dependency in task.inputs:
      if isinstance(dependency, BuildTask):
        self._find_dependencies(dependency)
    self.tasks.append(task)
    task._state = 'scheduled'
  
  def _expand_dependencies(self, task):
    expanded_inputs = []
    for dependency in task.inputs:
      if isinstance(dependency, BuildTask):
        expanded_inputs.extend(dependency.outputs)
      else:
        expanded_inputs.append(dependency)
    task.inputs = expanded_inputs
    
  def _needs_build(self, task):
    return False
  
  def _compute_md5(self, filenames):
    m = hashlib.md5()
    for filename in filenames:
      if not os.path.exists(filename):
        return None
      f = open(filename)
      m.update(f.read())
      f.close()
    return m.hexdigest()
    
  def _build_task(self, task):
    """Builds a single task. At this point all task dependencies has been built."""
    task.expand_outputs() # Allow dynamically generated files
    inputs_md5 = self._compute_md5(task.inputs)
    outputs_md5 = self._compute_md5(task.outputs)
    if not outputs_md5 or self.bld.state.get(outputs_md5, None) != inputs_md5:
        print '[%s] building %s -> %s' % (task.__class__.__name__, task.inputs, task.outputs)
        task.build()
        #self.bld.state[outputs_md5] = inputs_md5
        #self.bld.save_state()
    task.state = 'built'

    
  def _save_build(self, task):
    pass
